在前面我們實踐了 Login 並取得 JWT。
接著,在 Access 時,我們會需要 Send 一個 Auth request 給 Server,然後,Server 端會比較 JWT,最後回傳 response 給 Client 端。

在 access 的時候,會將 JWT 帶入 request header,接著,我們會藉由在 server 中比較 JWT,再返回 request。
取得 token
接收 Client 傳來的 header ,並取得 JWT
驗證 token
確認 user 是否還存在
避免在認證完 token 後,user 被 delete
確認是否在 issue 發出去後,user 改動了 password
Password 被更動,應該要重新取得 JWT
允許進入被受保護的 route
以下為範例:
protect router
// 會先運行 authController.protect,執行成功後再運行 testController.getAllTests (此處主要是保護 getAllTests 這個 routes)
router
  .route('/')
  .get(authController.protect, testController.getAllTests)
  .post(testController.createTest)
protect function:
exports.protect = async (req, res, next) => {
    
    // 1. 取得 token 
    let token;
    // 此處會依照開頭是否是 Bearer 來作為判斷依據的原因:
    // 因為這邊預設 request 的 header 是帶上: "Bearer ${JWT_TOKEN}" 
    if(
      req.headers.authentication &&
      req.headers.autrhntication.startWith('Bearer')
    ) {
        token = req.headers.authorization.split(' ')[1];
    }
    
    if (!token) {
        return next (
            new AppError('Please Log in to get access', 401);
        );
    }
    
    // 2. 驗證 token
    // 此處使用 promisify 是想要把 jwt.verify 轉換成 promise 版本,避免 Error first call-back
    const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET);
    
    
    // 3. 確認 user 是否還存在
    const freshUser = await User.findById(decoded.id);
    if(!freshUser) {
        return next(new AppError('The token belonging to this user does no longer exist.', 401));
    }
    
    // 4. 確認是否在 token 發出去後,user 改動了 password
    // 此處使用到的 changedPasswordAfter function 寫在下一個程式碼區塊
    // 這邊的 iat 是指 指示 JWT 發佈時間的時間戳,有興趣可以回之前的規劃篇看看 ~
    if (freshUser.changedPasswordAfter(decoded.iat)) {
        return next(new AppError('Password has been changed recently. Please login again.', 401));
    }
    
    // 5. 允許進入被受保護的 route
    req.user = freshUser;
    
    next();
    
}
changedPasswordAfter() function:
userSchema.methods.changedPasswordAfter = function(JWTTimestamp) {
    
    // 此處為原本有的 passwordChangedAt 參數
    if (this.passwordChangedAt) {
        
        const changedTimestamp =   parseInt(
            this.passwordChangedAt.getTime() / 1000, 10);
        
        return JWTTimestamp = changedTimestamp;
    }
    
    return false;
}
在上述程式碼中,我們並沒有特別處理 Error 的管理部分,
所以當出現了非預期的 Error,可能就整個系統 crash 了!
回顧我們先前的文章,還記得 Error Handling 的重要性吧XD
我們在上述程式碼說明了 JWT 的驗證實作,但我們都沒有處理 Error Handling! 這會是一個致命傷。
我們將會在下次一次說明 Error Handling 的問題,production 環境 & 非 production 環境 error message 的差異 ~
下次 Error Handling 感覺又可以寫一篇,不過感覺自己越學習越多越充實~ 雖然經驗還沒有很足,但我想可以越了解越多也是好事 :D
Udemy Node.js, Express, MongoDB & More: The Complete Bootcamp 2023
https://www.udemy.com/course/nodejs-express-mongodb-bootcamp/
Hacksplaining:
https://www.hacksplaining.com/